#include "SessionService_types.h"
#include "SessionService.h"

#include <iostream>
#include <map>

#include "base/logging.h"
#include "base/get_local_ip.h"
#include "base/stop_watch.h"
#include "base/config_reader.h"
#include "melon/service/service_arch.h"
#include "melon/service/base_service_impl.h"

#include "redis_executor/simple_redis_locator.h"
#include "redis_session_adapter.h"
#include "session_update_task.h"
#include "shop_waiter_map.h"

// using namespace std;
using namespace boost;
using namespace melon::service;
using namespace apache::thrift;
using apache::thrift::concurrency::Runnable;

class RedisLocator;

namespace imserver {

class SessionServiceHandler : public SessionServiceIf, public BaseServiceImpl {
 public:
  SessionServiceHandler() : redis_adapter_(new FixedLocator("127.0.0.1:6379")) {
  }
  SessionServiceHandler(RedisLocator * redis_locator) : redis_adapter_(redis_locator) {
  }
  virtual void AddSession(const int64_t id, const std::string& session) {
    LOG_INFO("AddSession " << session << '/' << id);
    GetThreadManager()->add(boost::shared_ptr<Runnable>(new SessionUpdateTask(redis_adapter_, id, session, SessionUpdateTask::OP_ADD)));
  }

  virtual void RemoveSession(const int64_t id, const std::string& session) {
    LOG_INFO("RemoveSession " << session << '/' << id);
    GetThreadManager()->add(boost::shared_ptr<Runnable>(new SessionUpdateTask(redis_adapter_, id, session, SessionUpdateTask::OP_REMOVE)));
  }

  virtual bool VerifySession(const int64_t id, const std::string& session) {
    if (id <= 0 || session.empty()) {
      return false;
    }
    base::StopWatch watch;
    bool ret = redis_adapter_.VerifySession(id, session);
    LOG_INFO("VerifySession id=" << id << " session=" << session 
             << " ret=" << ret << " time=" << watch);
    return ret;
  }

  virtual void GetSessionList(SessionList& _return, const int64_t id) {
    for(int i = 0; i < 2; ++i) {
      if (0 == redis_adapter_.GetSessionList(&_return, id)) {
        LOG_INFO("GetSessionList OK. uid=" << id);
        break;
      }
      LOG_WARN("GetSessionList ERR. uid=" << id);
    }
  }

  virtual void LinkTempIdQid(const int64_t temp_id, const int64_t qid) {
    for(int i = 0; i < 2; ++i) {
      if (redis_adapter_.LinkTempIdQid(temp_id, qid) == 0)
        break;
    }
  }

  virtual int64_t GetQidByTempId(const int64_t temp_id) {
    int qid = 0;
    for(int i = 0; i < 2; ++i) {
      qid = redis_adapter_.GetQidByTempId(temp_id);
      if (qid >= 0) {
        break;
      }
    }
    return qid;
  }

  virtual void GetOnlineUsers(std::map<int64_t, SessionType::type> & _return, 
      const std::vector<int64_t> & ids) {
    if (ids.empty()) {
      LOG_INFO("GetOnlineUsers empty ids.")
      return;
    }

    for(int i = 0; i < 2; ++i) {
      base::StopWatch watch;
      if (0 == redis_adapter_.GetOnlineUsers(ids, &_return)) {
        LOG_INFO("GetOnlineUsers OK. ids[0]=" << ids[0] << " ids.size=" << ids.size() 
                 << " res_size=" << _return.size() << " time=" << watch);
        break;
      }
      LOG_WARN("GetOnlineUsers ERR. ids[0]=" << ids[0] << " ids.size=" << ids.size() 
               << " time=" << watch);
    }
  }
 private:
  RedisSessionAdapter redis_adapter_;
};

class ProcessorFactoryWithLocator : public AbstractProcessorFactory {
 public:
  ProcessorFactoryWithLocator(FixedLocator * redis_locator) : redis_locator_(redis_locator) {
  }
  shared_ptr<TProcessor> Create() {
    shared_ptr<SessionServiceHandler> handler(new SessionServiceHandler(redis_locator_));
    shared_ptr<TProcessor> processor(new SessionServiceProcessor(handler));
    return processor;
  }
 private:
  RedisLocator * redis_locator_;
};

}

int main(int argc, char **argv) {
  char * conf_file = "../conf/session_service.conf";
  char opt;
  while ((opt = getopt(argc, argv, "c:")) != -1) {
    switch (opt) {
    case 'c':
      conf_file = optarg;
      break;
    default:
      std::cerr << "Unknown option " << optopt << std::endl;
      return 1;
    }
  }

  ConfigReader cfg(conf_file);
  if (!cfg) {
    std::cerr << "Config file " << conf_file << " read error!" << std::endl;
    return 1;
  }

  LOG_INIT(cfg.Get("log4cplus", "file"), cfg.Get("log4cplus", "level"));

  int port = cfg.GetWithType<int>("session_service", "port", 9092);
  LOG_INFO("listens on port " << port);

  using namespace imserver;
  ShopWaiterMap::Instance().Reload();

  ThriftService<SessionServiceHandler, SessionServiceProcessor> service;
  
  service.set_processor_factory(
      new ProcessorFactoryWithLocator(new FixedLocator(cfg.Get("redis", "server_list"))));

  service.StartRegister("/im/session_service", "1.0", 0, base::GetLocalIp(), port, 1);
  service.Start(port);
  return 0;
}

